package jmine.tec.di.injector.impl;

import java.lang.reflect.InvocationTargetException;

import jmine.tec.di.DependencyInjectionMessages;
import jmine.tec.di.exceptions.PropertyNotFoundException;
import jmine.tec.di.injector.Injector;
import jmine.tec.di.metadata.DependencyMetaData;
import jmine.tec.di.scope.TypedScope;
import jmine.tec.di.type.TypeConvertionException;

/**
 * Classe base para os {@link Injector} que invoquem metodos
 * 
 * @author takeshi
 */
public abstract class AbstractMethodBasedInjector implements Injector {

    private final DependencyMetaData meta;

    /**
     * @param meta {@link DependencyMetaData}
     */
    public AbstractMethodBasedInjector(DependencyMetaData meta) {
        super();
        this.meta = meta;
    }

    /**
     * {@inheritDoc}
     */
    public final void doInject(Object target, TypedScope properties) throws TypeConvertionException {
        try {
            if (this.meta.isResolvable(properties)) {
                final Object valueResolved = this.meta.resolveObjectWrap(properties);
                if (valueResolved != null) {
                    this.invokeMethod(target, valueResolved);
                } else if (this.meta.isRequired()) {
                    throw new PropertyNotFoundException(DependencyInjectionMessages.PROPRIEDADE_VAZIA.create(this.meta.getPropertyName()));
                }
            } else {
                if (this.meta.isRequired()) {
                    throw new PropertyNotFoundException(DependencyInjectionMessages.PROPRIEDADE_VAZIA.create(this.meta.getPropertyName()));
                }
            }
        } catch (InvocationTargetException e) {
            rethrowOrWrap(e);
        }

    }

    /**
     * Faz a invocacao do metodo propriamente dita
     * 
     * @param target o alvo
     * @param argument p argumento
     * @throws InvocationTargetException se houver uma excecao
     */
    protected abstract void invokeMethod(Object target, Object argument) throws InvocationTargetException;

    /**
     * @param e excecao
     */
    public static void rethrowOrWrap(InvocationTargetException e) {
        Throwable cause = e.getTargetException();
        if (cause instanceof Error) {
            throw ((Error) cause);
        } else if (cause instanceof RuntimeException) {
            throw ((RuntimeException) cause);
        } else {
            throw new IllegalStateException("Exception thrown on dependency injection", e);
        }
    }

    /**
     * @return {@link DependencyMetaData}
     */
    public final DependencyMetaData getDependencyMetaData() {
        return this.meta;
    }

}
